﻿using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using TencentCloud.Bmvpc.V20180625.Models;
using ZenSystemService.Api.Data;
using ZenSystemService.Api.Domain;
using ZenSystemService.Api.DTOs;
using ZenSystemService.Api.DTOs.CourseTrainingAssign;
using ZenSystemService.Api.DTOs.Product;
using ZenSystemService.Api.DTOs.ProductType;
using ZenSystemService.Api.DTOs.TraineeCourseTraining;
using ZenSystemService.Api.DTOs.TrainingItem;
using ZenSystemService.Api.Entities;

namespace ZenSystemService.Api.Services
{
    public class CourseTrainingAssignService
    {
        private readonly ApplicationDbContext _context;
        private readonly IMapper _mapper;

        public CourseTrainingAssignService(ApplicationDbContext context, IMapper mapper)
        {
            _context = context;
            _mapper = mapper;
        }

        /// <summary>
        /// 训练项结束，学员和客户端取消绑定
        /// </summary>
        /// <param name="req"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>

        public async Task<bool> RevokeAssignedUser(RevokeAssignedUserRequestDTO req)
        {
            if (req.TraineeCourseTrainingId > 0)
            {
                var training = await _context.TraineeCourseTrainings
                        .FirstOrDefaultAsync(tct => tct.TraineeCourseTrainingId == req.TraineeCourseTrainingId);

                if (training == null)
                    return true;
                training.Status = "0";
                await _context.SaveChangesAsync();
                return true;
            }
            else if(req.CourseId > 0 && req.TrainingItemId > 0) 
            {
                var cti = await _context.CourseTrainingItems.FirstOrDefaultAsync(cti => cti.CourseId == req.CourseId
                      && cti.TrainingItemId == req.TrainingItemId);
                if (cti != null)
                {
                    cti.CourseTrainingItemState = 0;
                    _context.CourseTrainingItems.Entry(cti).State = EntityState.Modified;
                    await _context.SaveChangesAsync();
                }
                var trainings = _context.TraineeCourseTrainings
                        .Where(tct => tct.CourseId == req.CourseId
                            && tct.TrainingItemId == req.TrainingItemId);

                if (trainings == null || trainings.Count() == 0)
                    return true;
                await trainings.ForEachAsync(t => t.Status = "0");
                await _context.SaveChangesAsync();
                return true;
            }
            throw new Exception("结束训练请求参数不正确");
        }

        public async Task<ReturnObjectT<IEnumerable<GetProductsByUsingStatusResponseDTO>>> GetProductsByUsingStatusAsync(ReceiveObjectT<GetProductsByUsingStatusRequestDTO> receiveObject)
        {
            var req = receiveObject.Data;
            var query = _context.Products.Where(p => p.InstitutionId == req.InstitutionId);
            if(req.AssignedToClient.HasValue && req.AssignedToClient.Value)
            {
                query = query.Where(p => p.Status == "1");
            }
            if(req.BeingUsedByTrainee.HasValue && req.BeingUsedByTrainee.Value)
            {
                query = query.Where(p => !_context.TraineeCourseTrainings.Any(tct => tct.ProductId == p.ProductId
                    && tct.Status == "1"));
            }
            if(req.TrainingItemId.HasValue && req.TrainingItemId.Value > 0)
            {
                var trainingItem = await _context.TrainingItems.FirstOrDefaultAsync(
                    ti => ti.TrainingItemId == req.TrainingItemId);
                if(trainingItem != null)
                {
                    query = query.Where(p => p.ProductTypeId == trainingItem.ProductTypeId);
                }
            }
            var product_training = from p in query
                        join tct in _context.TraineeCourseTrainings
                        on p.ProductId equals tct.ProductId
                        into p_tct
                        from tct in p_tct.DefaultIfEmpty()
                        group tct by p into p_group
                        select new
                        {
                            Product = p_group.Key,
                            TraineeCourseTraining = p_group.FirstOrDefault()
                        };
            var skip = (receiveObject.Page - 1) * receiveObject.PageSize;
            var take = receiveObject.PageSize;
            var totalRecords = await product_training.CountAsync();
            var productList = await product_training.Skip(skip).Take(take).ToListAsync();

            var result = new List<GetProductsByUsingStatusResponseDTO>();
            foreach(var p in productList)
            {
                result.Add(new GetProductsByUsingStatusResponseDTO
                {
                    Product = _mapper.Map<ProductQueryResultDTO>(p.Product),
                    TraineeCourseTraining = _mapper.Map<TraineeCourseTrainingQueryResultDTO>(p.TraineeCourseTraining),
                });
            }

            return new ReturnObjectT<IEnumerable<GetProductsByUsingStatusResponseDTO>>
            {
                Code = 1,
                Data = result,
                RecordTotalCount = totalRecords
            };
        }

        /// <summary>
        /// 当前这个客户端分配给哪一个学员了
        /// </summary>
        /// <param name="products">Token里面的序列码查出来的产品列表</param>
        /// <returns></returns>

        public async Task<GetAssignedUserResponseDTO> GetAssignedUser(GetAssignedUserRequestDTO req)
        {
            if (string.IsNullOrEmpty(req.UniqueNumber))
                throw new Exception("没有提供序列码");
            var product = _context.Products.FirstOrDefault(p => p.UniqueNumber == req.UniqueNumber);
            if (product == null)
                throw new Exception("根据序列码找不到产品");
            //先把对应的Product最后访问时间更新一下
            product.LastVisitedDate = DateTime.Now;
            _context.Products.Entry(product).State = EntityState.Modified;
            await _context.SaveChangesAsync();
            
            //先把在UniqueNumber列表中的训练项取出来
            var trainings = _context.TraineeCourseTrainings
                    .Include(tct => tct.Product)
                    .Include(tct => tct.Trainee)
                    .Include(tct => tct.Course)
                    .Include(tct => tct.TrainingItem)
                    .Where(tct => tct.Status == "1" && req.UniqueNumber == tct.Product.UniqueNumber).ToList();

            //其中一个Product的使用人就是使用人
            var training  = trainings.FirstOrDefault();
            if (training == null || training.Trainee == null)
                throw new Exception("找不到使用人");
            var traineeDTO = _mapper.Map<UserDTO>(training.Trainee);
            traineeDTO.Birthday = training.Trainee.Birthday?.ToString("yyyy/MM/dd");
            var courseDTO = _mapper.Map<CourseQueryResultDTO>(training.Course);
            var trainingItemDTO = _mapper.Map<TrainingItemQueryResultDTO>(training.TrainingItem);
            return new GetAssignedUserResponseDTO
            {
                Trainee = traineeDTO,
                Course = courseDTO,
                TrainingItem = trainingItemDTO,
                TraineeCourseTrainingId = training.TraineeCourseTrainingId,
            };
        }

        /// <summary>
        /// 教练点开始上课，分配学员到软件客户端，软件客户端是绑定的产品序列码决定的
        /// </summary>
        /// <param name="loginUser"></param>
        /// <param name="req"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<IEnumerable<TraineeCourseTrainingQueryResultDTO>> AssignTraineeToClients(UserModel loginUser, AssignTraineeToProductsRequestDTO req)
        {
            if (req.ProductTrainees == null || req.ProductTrainees.Count() == 0)
                throw new Exception("参数错误，没有指定要分配的产品和学员");
            //再次检查Product是否被占用
            var toAssignProductList = req.ProductTrainees.Select(pt => pt.ProductId).ToList();
            var beingUsedProducts = _context.TraineeCourseTrainings
                .Include(tct => tct.Product)
                .Where(tct => toAssignProductList.Contains(tct.ProductId.Value) 
                    && tct.Status == "1")
                .ToList();
            var beingUsedProductNames = beingUsedProducts
                .Select(tct => tct.Product.Alias ?? $"{{{tct.ProductId}}}")
                .ToArray();
            if(beingUsedProducts.Any())
            {
                throw new Exception("产品正在被使用：" + string.Join(',', beingUsedProductNames));
            }

            IEnumerable<TraineeCourseTrainingQueryResultDTO> result = null;
            using (var dbTransaction = await _context.Database.BeginTransactionAsync())
            {
                
                try
                {
                    //更新课程训练项状态为已开始
                    var courseTrainingItem = _context.CourseTrainingItems.FirstOrDefault(cti => cti.CourseId == req.CourseId
                        && cti.TrainingItemId == req.TrainingItemId);
                    if (courseTrainingItem == null)
                        throw new Exception("找不到课程训练项信息");
                    courseTrainingItem.CourseTrainingItemState = 1;
                    courseTrainingItem.Count += 1;
                    _context.CourseTrainingItems.Entry(courseTrainingItem).State = EntityState.Modified;
                    await _context.SaveChangesAsync();

                    var traineeCourseTrainings = req.ProductTrainees.Select(pt =>
                        new TraineeCourseTraining
                        {
                            CourseId = req.CourseId,
                            TrainingItemId = req.TrainingItemId,
                            ProductId = pt.ProductId,
                            TraineeId = pt.TraineeId,
                            Status = "1",
                            CreatedDate = DateTime.Now
                        });
                    await _context.TraineeCourseTrainings.AddRangeAsync(traineeCourseTrainings);
                    await _context.SaveChangesAsync();
                    if (traineeCourseTrainings != null && traineeCourseTrainings.Any())
                        result = _mapper.Map<IEnumerable<TraineeCourseTrainingQueryResultDTO>>(traineeCourseTrainings);
                    await dbTransaction.CommitAsync();
                }
                catch (Exception ex)
                {
                    await dbTransaction.RollbackAsync();
                    throw new Exception("分配学员到产品失败:" + ex.Message, ex);
                }                
            }
            
            return result;
        }

        /// <summary>
        /// 教练点开始上课，分配学员到软件客户端，软件客户端是绑定的产品序列码决定的
        /// </summary>
        /// <param name="loginUser"></param>
        /// <param name="req"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<IEnumerable<TraineeCourseTrainingQueryResultDTO>> AssignTraineeToClient(UserModel loginUser, AssignTraineeToProductRequestDTO req)
        {
            //更新课程状态为已开始
            var courseTrainingItem = _context.CourseTrainingItems.FirstOrDefault(cti => cti.CourseId == req.CourseId
                && cti.TrainingItemId == req.TrainingItemId);
            if (courseTrainingItem == null)
                throw new Exception("找不到课程训练项信息");
            courseTrainingItem.CourseTrainingItemState = 1;
            courseTrainingItem.Count += 1;
            _context.CourseTrainingItems.Entry(courseTrainingItem).State = EntityState.Modified;
            await _context.SaveChangesAsync();


            var trainees = await _context.Users.Where(u => req.Trainees.Contains(u.UserId)).ToListAsync();

            //找到学员列表
            //   根据训练项目找到这个训练项目需要的产品类型
            //      根据产品类型找到未被标记的产品
            //   最终记录的结果是
            //      学员，课程Id, 训练项目Id, 使用产品Id

            var assignResult = new List<TraineeCourseTraining>();

            //如果有产品还在占用，强制把所有产品释放掉
            //(把学员课程训练表的所有记录的Status改成0）
            /*var beingUsedProduct = _context.TraineeCourseTrainings.Where(tct => tct.Status == "1");
            await beingUsedProduct.ForEachAsync(t =>
            {
                t.Status = "0";
            });
            await _context.SaveChangesAsync();*/

            //开始重新分配
            //先把所有已下发过序列码的产品都找到找到

            var labeledProducts = _context.Products.Where(p => p.Enabled == "1"
                && p.Status == "1" && p.InstitutionId == req.InstitutionId);

            var trainingItem = _context.TrainingItems.Include(ti => ti.ProductType)
                    .FirstOrDefault(ti => ti.TrainingItemId == req.TrainingItemId);
            if (trainingItem == null)
                throw new Exception("通过训练项找不到产品类型");

            var availableProducts = labeledProducts.Where(lp => lp.ProductTypeId == trainingItem.ProductTypeId);

            //这些产品没有正在被使用
            var beingUsedProductIds = await _context.TraineeCourseTrainings.Where(tct =>
                availableProducts.Any(ap => ap.ProductId == tct.ProductId) && tct.Status == "1")
                .Select(tct => tct.ProductId).ToArrayAsync();

            //availableProducts = availableProducts.Where(ap => !beingUsedProductIds.Any(bp => bp == ap.ProductId));

            var availableProductList = await availableProducts.ToListAsync();
            if (availableProductList == null || availableProductList.Count() == 0)
            {
                throw new Exception($"目前没有可供使用的产品");
            }
            if (availableProductList.Count() < trainees.Count)
            {
                throw new Exception($"学员数大于可用产品数");
            }
            for (int i = 0; i < trainees.Count; i++)
            {
                assignResult.Add(new TraineeCourseTraining
                {
                    CourseId = req.CourseId,
                    TraineeId = trainees[i].UserId,
                    TrainingItemId = req.TrainingItemId,
                    ProductId = availableProductList[i].ProductId,
                    Status = "1",
                });
            }
            await _context.TraineeCourseTrainings.AddRangeAsync(assignResult);
            await _context.SaveChangesAsync();
            return _mapper.Map<IEnumerable<TraineeCourseTrainingQueryResultDTO>>(assignResult);
        }
    }
}
